from django.db import models
from django.contrib.auth.models import User
from django.contrib.auth.models import BaseUserManager, AbstractBaseUser, UserManager
from django.contrib.auth.hashers import (
    check_password, make_password, is_password_usable, UNUSABLE_PASSWORD)
from datetime import datetime
from django.contrib.auth.hashers import make_password
from django.contrib.auth.models import BaseUserManager, User
from django.db import models
from cashgirls import settings
import random
from forms import RestrictedFileField, RestrictedImageField
    
#CONSTANT for request status
class STATUS:
    ON_REVIEW, PENDING_ANSWER, ACCEPTED, REJECTED_BY_ADMIN, REJECTED_BY_USER, DELIVERY_PENDING, DELIVERY_ON_REVIEW, DELIVERED, EXPIRED = range(0,9)
    choices = {
          ON_REVIEW: 'On review',
          PENDING_ANSWER: 'Pending answer',
          ACCEPTED: "Accepted",
          REJECTED_BY_ADMIN: "Rejected by Admin",
          REJECTED_BY_USER: "Rejected by User",
          DELIVERY_PENDING: "Delivery pending",
          DELIVERY_ON_REVIEW: "Delivery on review",
          DELIVERED: "Delivered",
          EXPIRED: "Expired",}

class USER_TYPE:
    ADMIN, MERCHANT, SHOPPER = range(0,3)
    
class BANK_ACCOUNT_TYPE:
    SAVING, CHECKING = range(0,2)
    choices = {
          SAVING: 'Saving',
          CHECKING: 'Checking',
          }

class RETURN_STATUS:
    OBJECT_NOT_FOUND, BAD_REQUEST, OK = range(0,3)
    choices = {
               OBJECT_NOT_FOUND: 'NOT FOUND',
               BAD_REQUEST: "BAD REQUEST",
               OK: "OK",
               }

class PAYMENT_STATUS:
    PENDING, PAID, REJECTED, CANCELED = range(0,4)
    choices = {
               PENDING: 'PENDING',
               REJECTED: 'REJECTED',
               PAID: "PAID",
               CANCELED: "CANCELED",
              }

class ACCEPTED_LOCALE:
    EN, JA, ES = range(0,3)
    choices = {
              EN: 'ENGLISH',
              JA: 'JAPANESE',
              ES: 'SPANISH',
            }

    @staticmethod
    def get_constant(name):
        i = 0
        for el in ACCEPTED_LOCALE.__dict__.keys():
            if el == name:
                return i
            if el.isupper():
                i = i + 1
        raise Exception('Locale not found', name)
    
    @staticmethod
    def get_lang_code(index):
        for el in ACCEPTED_LOCALE.__dict__.keys():
            if el.isupper():
                if index == 0:
                    return el
                else:
                    index = index - 1
        raise Exception('Locale not found for index', index)            

# cashG User handler all the implementations related to user has to be done throw this API
class CashGUserManager(BaseUserManager):
    
    def create_merchant_user(self, username, email, password, lang, age=None):
        merchantUser = MerchantUser()    
        merchantUser.age=age    
        merchantUser.password = make_password(password)
        merchantUser.email = email
        merchantUser.username = username  
        merchantUser.lang = lang
        merchantUser.save(using=self._db)
        return merchantUser
    
    def create_shopper_user(self, username, email, password, lang):
        shopperUser = ShopperUser()    
        shopperUser.password = make_password(password)
        shopperUser.email = email
        shopperUser.username = username 
        shopperUser.lang = lang
        shopperUser.save(using=self._db)
        return shopperUser

class AbstactCashGModel(models.Model):
    cretion_date = models.DateTimeField(auto_now_add=True)
    update_date = models.DateTimeField(auto_now=True)

class AbstractUser(User):
    user = models.OneToOneField(User)
    points = models.IntegerField(default=0)
    confirmed = models.BooleanField(default=False)
    creation_date = models.DateTimeField(auto_now_add=True)
    update_date = models.DateTimeField(auto_now=True)
    lang = models.SmallIntegerField()
    objects = CashGUserManager()
    
    def getActiveProfile(self):
        profiles = self.profiles.all()
        for prof in profiles:
            if prof.status==STATUS.ACCEPTED:
                return prof
        return None
    
    def getPendingProfile(self):
        profiles = self.profiles.filter(status__in=[STATUS.ON_REVIEW,STATUS.REJECTED_BY_ADMIN])
        if profiles:
            return profiles[0]
        return None
 
class BankAccount(AbstactCashGModel):
    bank_name = models.CharField(max_length=250, blank=False, null=False)
    holder_name = models.CharField(max_length=250, blank=False, null=True)
    account_number = models.CharField(max_length=250, blank=False, null=True)
    account_type = models.IntegerField(default=BANK_ACCOUNT_TYPE.CHECKING)
    swift = models.CharField(max_length=11, blank=True, null=True)
    
# Model for the merchant User
class MerchantUser(AbstractUser):
    age = models.IntegerField()
    profile_image = models.IntegerField(blank=True, null=True)
    bank = models.OneToOneField(BankAccount, null=True)
    objects = CashGUserManager()
 
    
class AbstractProfile(AbstactCashGModel):
    first_name = models.CharField(max_length=30, blank=True)
    last_name = models.CharField(max_length=30, blank=True)
    status = models.IntegerField(default=STATUS.ON_REVIEW)
    status_comment = models.TextField()
    
    def getLocProfile(self, locale, force=False):
        first_el=None
        for loc_profile in self.loc_profiles.all():
            if not first_el:
                first_el = loc_profile
            if loc_profile.lang == locale:
                return loc_profile
        if force:
            return first_el
        return None

class LocalizedModel(AbstactCashGModel):
    profile = models.ForeignKey(AbstractProfile, related_name='loc_profiles')
    lang = models.SmallIntegerField(blank=False, null=False)
    loc_profile = models.TextField()
    

class MerchantProfile(AbstractProfile):
    merchantUser = models.ForeignKey(MerchantUser, related_name='profiles')

# Model for the shopper User
class ShopperUser(AbstractUser):
    #user = models.OneToOneField(AbstractUser)
    profile = models.TextField()
    objects = CashGUserManager()
    image = models.ImageField(upload_to= lambda instance, filename: 'documents/{0}/{1}'.format(instance.user.username, filename), blank=True, null=True)
    thumbnail = models.ImageField(upload_to= lambda instance, filename: 'documents/{0}/thumbnail/{1}'.format(instance.user.username, filename), blank=True, null=True)
    objects = CashGUserManager()

class ShopperProfile(AbstractProfile):
    shopperUser = models.ForeignKey(ShopperUser, related_name='profiles')
    
# Images related with the profile
class ProfileImage(models.Model):
    profile = models.ForeignKey(AbstractProfile, related_name='images')
    image = models.ImageField(upload_to= lambda instance, filename: 'documents/{0}/{1}'.format(instance.profile.id, filename), blank=True, null=True)
    thumbnail = models.ImageField(upload_to= lambda instance, filename: 'documents/{0}/thumbnail/{1}'.format(instance.profile.id, filename), blank=True, null=True)

    def save(self, force_insert=False, force_update=False, using=None):
        # create a thumbnail
        create_thumbnail(self)
        super(ProfileImage, self).save()
   

class Request(AbstactCashGModel):
    sender = models.ForeignKey(ShopperUser, related_name='sentRequests')
    receiver = models.ForeignKey(MerchantUser, related_name='receivedRequests')
    status = models.IntegerField(default=STATUS.ON_REVIEW)
    price =  models.IntegerField(blank=True, null=True)
    # Maybe these fields should be in a separate model, but for simplicity let's leave it here
    rejection_comment = models.CharField(max_length=160)
    request_text = models.TextField(db_column='request');
    expiration_date = models.DateTimeField(blank=True, null=True)

    # This function set expired status to a request when is called and the request is expired
    def _expirer(self):
        if self.expiration_date:
            if self.expiration_date.replace(tzinfo=None) < datetime.now():
                self.status = STATUS.EXPIRED
                self.save()
        return self

    @staticmethod
    def all():
        requests = Request.objects.all()
        return map(lambda r: r._expirer(), requests)
     
    @staticmethod   
    def expireRequests(requests):
        return map(lambda r: r._expirer(), requests)
    
class Purchase(AbstactCashGModel):
    request = models.ForeignKey(Request, related_name='linkedPurchase')
    asset = models.ImageField(upload_to= lambda instance, filename: 'documents/{0}/{1}'.format(random.getrandbits(128), filename), blank=True, null=True)
    expiration_date = models.DateTimeField()

# static function for thumbnail creation
def create_thumbnail(imageContainer):
    if not imageContainer.image:
        return

    import Image
    from cStringIO import StringIO
    from django.core.files.uploadedfile import SimpleUploadedFile
    import os
    THUMBNAIL_SIZE = (150, 150)

    DJANGO_TYPE = imageContainer.image.file.content_type

    if DJANGO_TYPE == 'image/jpeg':
        PIL_TYPE = 'jpeg'
        FILE_EXTENSION = 'jpg'
    elif DJANGO_TYPE == 'image/png':
        PIL_TYPE = 'png'
        FILE_EXTENSION = 'png'

    # limit to jpeg, png
    image = RestrictedFileField(content_types=['image/jpeg', 'image/png'])
    # limit to 1MB 
    image = RestrictedImageField(settings.MAX_UPLOAD_SIZE)

    image = Image.open(StringIO(imageContainer.image.read()))
    image.thumbnail(THUMBNAIL_SIZE, Image.ANTIALIAS)

    # Save the thumbnail
    temp_handle = StringIO()
    image.save(temp_handle, PIL_TYPE)
    temp_handle.seek(0)

    suf = SimpleUploadedFile(os.path.split(imageContainer.image.name)[-1], temp_handle.read(), content_type=DJANGO_TYPE)
    imageContainer.thumbnail.save('%s_thumbnail.%s' % (os.path.splitext(suf.name)[0], FILE_EXTENSION), suf, save=False)     
    
class Payment(AbstactCashGModel):
    merchant = models.ForeignKey(MerchantUser, related_name='payments')
    amount = models.IntegerField(blank=False, null=False)
    status = models.IntegerField(default=PAYMENT_STATUS.PENDING)
    rejection_comment = models.CharField(max_length=160)
    # This field is just a snapshot of the bank where the payment is done
    bank_info_blob = models.CharField(max_length=256)
    
    def getStatus(self):
        return PAYMENT_STATUS.choices[self.status]
    
    def isCanceled(self):
        return self.status == PAYMENT_STATUS.CANCELED
    
    def isPending(self):
        return self.status == PAYMENT_STATUS.PENDING

